package com.gec.zhs.controller;

import cn.hutool.core.util.StrUtil;
import com.gec.zhs.annotation.ZhsLogin;
import com.gec.zhs.component.RedisComponent;
import com.gec.zhs.entity.config.AppConfig;
import com.gec.zhs.entity.constants.Constants;
import com.gec.zhs.entity.dto.SysSettingDto;
import com.gec.zhs.entity.dto.TokenUserInfoDto;
import com.gec.zhs.entity.dto.UploadingFileDto;
import com.gec.zhs.entity.dto.VideoPlayInfoDto;
import com.gec.zhs.entity.enums.DateTimePatternEnum;
import com.gec.zhs.entity.enums.FileTypeEnum;
import com.gec.zhs.entity.enums.ResponseCodeEnum;
import com.gec.zhs.entity.pojo.VideoInfoFile;
import com.gec.zhs.entity.vo.ResponseVO;
import com.gec.zhs.exception.BusinessException;
import com.gec.zhs.service.VideoInfoFileService;
import com.gec.zhs.utils.DateUtil;
import com.gec.zhs.utils.FFmpegUtils;
import com.gec.zhs.utils.StringTools;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.*;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @author zhs
 * @version 1.0
 * @date 2025-02-07
 * @description 文件上传下载
 */
@RestController
@RequestMapping("/file")
@Validated
@Slf4j
public class FileController extends ABaseController {
    @Autowired
    private AppConfig appConfig;

    @Autowired
    private RedisComponent redisComponent;

    @Autowired
    private FFmpegUtils fFmpegUtils;

    @Autowired
    private VideoInfoFileService videoInfoFileService;


    @ApiOperation("获取图片")
    @RequestMapping("/getResource")
    public void getResource(HttpServletResponse response, String sourceName) {
        if (!StringTools.pathIsOk(sourceName)) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        String suffix = StringTools.getFileSuffix(sourceName);
        FileTypeEnum fileTypeEnum = FileTypeEnum.getBySuffix(suffix);
        if (null == fileTypeEnum) {
            throw new BusinessException(ResponseCodeEnum.CODE_600);
        }
        switch (fileTypeEnum) {
            case IMAGE:
                //缓存30天
                response.setHeader("Cache-Control", "max-age=" + 30 * 24 * 60 * 60);
                response.setContentType("image/" + suffix.replace(".", ""));
                break;
        }
        readFile(response, sourceName);
    }

    @ApiOperation("预上传文件id")
    @RequestMapping("/preUploadVideo")
    public ResponseVO preUploadVideo(String fileName, Integer chunks) {
        TokenUserInfoDto tokenUserInfoDto = getTokenUserInfoDto();
        String uploadId = redisComponent.savePreVideoFileInfo(tokenUserInfoDto.getUserId(), fileName, chunks);
        return getSuccessResponseVO(uploadId);
    }

    @ApiOperation("上传视频")
    @RequestMapping("/uploadVideo")
    @ZhsLogin(checkLogin = true)
    public ResponseVO uploadVideo(MultipartFile chunkFile, Integer chunkIndex, String uploadId) throws IOException {
        TokenUserInfoDto tokenUserInfoDto = getTokenUserInfoDto();
        UploadingFileDto uploadingVideoFile = redisComponent.getUploadingVideoFile(tokenUserInfoDto.getUserId(), uploadId);
        if (uploadingVideoFile == null) {
            throw new BusinessException(ResponseCodeEnum.CODE_604);
        }
        SysSettingDto sysSettingDto = redisComponent.getSysSettingDto();
        if (uploadingVideoFile.getFileSize() > sysSettingDto.getVideoSize() * Constants.MB_SIZE) {
            throw new BusinessException(ResponseCodeEnum.CODE_605);
        }
        //分片上传,index对应chunk
        if ((chunkIndex - 1) > uploadingVideoFile.getChunks() || chunkIndex > uploadingVideoFile.getChunks() - 1) {
            throw new BusinessException(ResponseCodeEnum.CODE_606);
        }
        String folder = appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_FOLDER_TEMP + uploadingVideoFile.getFilePath();
        File file = new File(folder + "/" + chunkIndex);
        chunkFile.transferTo(file);
        //记录上传视频的分片和视频大小
        uploadingVideoFile.setChunkIndex(chunkIndex);
        uploadingVideoFile.setFileSize(uploadingVideoFile.getFileSize() + chunkFile.getSize());
        redisComponent.updateVideoFileInfo(tokenUserInfoDto.getUserId(), uploadingVideoFile);
        return getSuccessResponseVO(null);
    }

    @ApiOperation("删除文件")
    @RequestMapping("/delUploadVideo")
    @ZhsLogin(checkLogin = true)
    public ResponseVO delUploadVideo(String uploadId) throws IOException {
        TokenUserInfoDto tokenUserInfoDto = getTokenUserInfoDto();
        UploadingFileDto uploadingVideoFile = redisComponent.getUploadingVideoFile(tokenUserInfoDto.getUserId(), uploadId);
        if (uploadingVideoFile == null) {
            throw new BusinessException(ResponseCodeEnum.CODE_604);
        }
        redisComponent.delVideoFileInfo(tokenUserInfoDto.getUserId(), uploadId);
        FileUtils.deleteDirectory(new File(appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_FOLDER_TEMP + uploadingVideoFile.getFilePath()));
        return getSuccessResponseVO(null);
    }

    @ApiOperation("图片上传")
    @RequestMapping("/uploadImage")
    @ZhsLogin(checkLogin = true)
    public ResponseVO uploadCover(MultipartFile file, Boolean createThumbnail) throws IOException {
        String month = DateUtil.format(new Date(), DateTimePatternEnum.YYYYMM.getPattern());
        String folder = appConfig.getProjectFolder() + Constants.FILE_FOLDER + Constants.FILE_COVER + month;
        File folderFile = new File(folder);
        if (!folderFile.exists()) {
            folderFile.mkdirs();
        }
        String fileName = file.getOriginalFilename();
        String fileSuffix = fileName.substring(fileName.lastIndexOf("."));
        String realFileName = StringTools.getRandomString(Constants.LENGTH_30) + fileSuffix;
        String filePath = folder + "/" + realFileName;
        file.transferTo(new File(filePath));
        if (createThumbnail) {
            //生成缩略图
            fFmpegUtils.createImageThumbnail(filePath);
        }
        return getSuccessResponseVO(Constants.FILE_COVER + month + "/" + realFileName);
    }

    /**
     * 返回stream
     *
     * @param response
     * @param fileId
     */
    @ApiOperation("nginx代理暴露和获取m3u8文件")
    @RequestMapping("/videoResource/{fileId}")
    public void videoResourceM3U8(HttpServletResponse response, @PathVariable String fileId) {
        VideoInfoFile videoInfoFile = videoInfoFileService.getVideoInfoFileByFileId(fileId);
        String filePath = videoInfoFile.getFilePath();
        //读取m3u8文件，视频播放顺序
        readFile(response, filePath + "/" + Constants.M3U8_NAME);

        /**
         * 24小时热门功能的需要
         * 播放视频时更新视频的信息，包括播放量，点赞投币等
         * 消息队列异步更新视频的数据
         */
        VideoPlayInfoDto videoPlayInfoDto = new VideoPlayInfoDto();
        videoPlayInfoDto.setVideoId(videoInfoFile.getVideoId());
        videoPlayInfoDto.setFileIndex(videoInfoFile.getFileIndex());
        TokenUserInfoDto tokenUserInfoDto = getTokenInfoFromCookie();
        System.out.println("tokenUserInfoDto:"+tokenUserInfoDto);
        if(StrUtil.isNotBlank(tokenUserInfoDto.getUserId())){
            videoPlayInfoDto.setUserId(tokenUserInfoDto.getUserId());
        }
        redisComponent.addVideoPlay(videoPlayInfoDto);
    }

    /**
     * @param response
     * @param fileId
     * @param ts
     */
    @ApiOperation("获取ts文件视频")
    @RequestMapping("/videoResource/{fileId}/{ts}")
    public void videoResourceTS(HttpServletResponse response, @PathVariable String fileId, @PathVariable String ts) {
        VideoInfoFile videoInfoFile = videoInfoFileService.getVideoInfoFileByFileId(fileId);
        String filePath = videoInfoFile.getFilePath();
        //读取ts文件，播放视频
        readFile(response, filePath + "/" + ts);
    }

    /**
     * 读取文件
     *
     * @param response
     * @param filePath
     */
    protected void readFile(HttpServletResponse response, String filePath) {
        File file = new File(appConfig.getProjectFolder() + Constants.FILE_FOLDER + filePath);
        if (!file.exists()) {
            return;
        }
        long startTime = System.currentTimeMillis();
        try (OutputStream out = response.getOutputStream();

             BufferedInputStream in = new BufferedInputStream(new FileInputStream(file))) {
            byte[] byteData = new byte[1024];
            int len = 0;
            while ((len = in.read(byteData)) != -1) {
                out.write(byteData, 0, len);
            }
            out.flush();
        } catch (IOException e) {
            log.error("读取文件异常", e);
        } finally {
            long endTime = System.currentTimeMillis();
            log.info("文件传输完成，耗时: {}ms", endTime - startTime);
        }
    }

}
